#include "MapBuilder.h"

#include <rapidxml.hpp>
#include <rapidxml_utils.hpp>
#include <fstream>
#include <memory>
#include <cstdio>
#include <vector>
#include "vec2.h"
#include "Polygon.h"

namespace
{
	const char* GetAttribute(rapidxml::xml_node<char>* node, const char* name)
	{
		rapidxml::xml_attribute<char>* attr = node->first_attribute(name);
		if(attr)
			return attr->value();
		throw std::runtime_error("Reading attribute '" + std::string(name) + "' in node '" + std::string(node->name()) + "' failed!");
	}

	double GetDoubleAttribute(rapidxml::xml_node<char>* node, const char* name)
	{
		const char* value = GetAttribute(node, name);
		char* end;
		double d = strtod(value, &end);
		if(end != value)
			return d;
		throw std::runtime_error("Parsing attribute '" + std::string(name) + "' with value '" + std::string(value) + "' failed!");
	}

	vec2f GetPosition(rapidxml::xml_node<char>* node)
	{
		return vec2f(GetDoubleAttribute(node, "x"), GetDoubleAttribute(node, "y"));
	}

	void ReadPolygonNode(rapidxml::xml_node<char>* polygonnode, MapBuilder& builder)
	{
		// extract texture
		const char* texture = GetAttribute(polygonnode, "texture");
		builder.AddTexturePath(texture);

		std::vector<vec2f> points;

		// extract position:
		rapidxml::xml_node<char>* posnode = polygonnode->first_node("position");
		for(auto posnode = polygonnode->first_node("position"); posnode != nullptr; posnode = posnode->next_sibling("position"))
		{
			points.push_back(GetPosition(posnode));
		}

		if(points.size() >= 3)
		{
			// add polygon
			builder.AddPolygon(points, texture);
		}
	}
}

MapBuilder::MapBuilder(const std::string& filepath)
{
	rapidxml::file<char> xmlFile(filepath.c_str());
    rapidxml::xml_document<char> doc;
	doc.parse<0>(xmlFile.data());

	rapidxml::xml_node<char>* polygons = doc.first_node("polygons");
	rapidxml::xml_node<char>* polygon = polygons->first_node("polygon");

	for(auto polygon = polygons->first_node("polygon"); polygon != nullptr; polygon = polygon->next_sibling("polygon"))
	{
		ReadPolygonNode(polygon, *this);
	}

}

void MapBuilder::AddTexturePath(const std::string& path)
{
	m_TexturePathes.push_back(path);
}

void MapBuilder::AddPolygon(const std::vector<vec2f>& points, const std::string& texturepath)
{
	if(points.size() >= 3)
	{
		m_Polygons.push_back(ftb::Polygon(points, texturepath));
	}
}

const std::list<std::string>& MapBuilder::GetTexturePathes() const
{
	return m_TexturePathes;
}

const std::list<ftb::Polygon>& MapBuilder::GetPolygons() const
{
	return m_Polygons;
}